package service

import (
	"reflect"

	"github.com/emirpasic/gods/maps/linkedhashmap"

	nexport "nggs/export"
)

type GroupConfig struct {
	GlobalConfig nexport.IServiceGlobalConfig
	Config       nexport.IServiceConfig
}

type Group struct {
	GroupConfig

	Name string

	GlobalConfigReflectType reflect.Type
	ConfigReflectType       reflect.Type

	GlobalConfig nexport.IServiceGlobalConfig
	Infos        *linkedhashmap.Map

	lastPickInstanceCursor int
	lastPickServiceID      int
}

func NewGroup(name string) *Group {
	sg := &Group{
		Name:                   name,
		Infos:                  linkedhashmap.New(),
		lastPickInstanceCursor: -1,
	}
	return sg
}

func (sg *Group) AddInfo(serviceID int, info *Info) {
	sg.Infos.Put(serviceID, info)
}

func (sg *Group) RemoveInfo(serviceID int) {
	sg.Infos.Remove(serviceID)
}

func (sg *Group) GetInfoByID(serviceID int) *Info {
	v, ok := sg.Infos.Get(serviceID)
	if !ok {
		return nil
	}
	return v.(*Info)
}

func (sg *Group) RoundRobinPickInstance() *Info {
	var info *Info
	instances := sg.Infos.Select(func(key interface{}, value interface{}) bool {
		i := value.(*Info)
		if i != nil && i.Instance != nil && !i.Instance.IsEmpty() {
			return true
		}
		return false
	})
	switch instances.Size() {
	case 0:
		return nil
	case 1:
		info := instances.Values()[0].(*Info)
		return info
	default:
		size := instances.Size()
		values := instances.Values()
		info = values[0].(*Info)
		cursor := sg.lastPickInstanceCursor + 1
		for ; cursor < size; cursor++ {
			info = values[cursor].(*Info)
			if info != nil && info.Instance != nil && !info.Instance.IsEmpty() {
				break
			}
		}
		if cursor >= size-1 {
			sg.lastPickInstanceCursor = -1
		} else {
			sg.lastPickInstanceCursor = cursor
		}
		if info == nil || info.Instance == nil || info.Instance.IsEmpty() {
			return nil
		}
		return info
	}
}

func (sg *Group) EachInfo(f func(int, *Info) bool) {
	sg.Infos.All(func(k, v interface{}) bool {
		return f(k.(int), v.(*Info))
	})
}

func (sg *Group) GetConfigByID(serviceID int) nexport.IConfig {
	s := sg.GetInfoByID(serviceID)
	if s == nil {
		return nil
	}
	return s.Config
}

func (sg *Group) EachConfig(f func(nexport.IConfig) bool) {
	sg.EachInfo(func(id int, info *Info) bool {
		return f(info.Config)
	})
}

func (sg *Group) GetInstance(serviceID int) *Instance {
	s := sg.GetInfoByID(serviceID)
	if s == nil {
		return nil
	}
	return s.Instance
}

func (sg *Group) EachInstance(f func(string, int, *Instance) bool) {
	sg.EachInfo(func(id int, info *Info) bool {
		return f(sg.Name, id, info.Instance)
	})
}

func (sg *Group) GetPayloadByID(serviceID int) int {
	s := sg.GetInfoByID(serviceID)
	if s == nil {
		return -1
	}
	return s.Payload
}

func (sg *Group) EachPayload(f func(string, int, int) bool) {
	sg.EachInfo(func(id int, info *Info) bool {
		return f(sg.Name, id, info.Payload)
	})
}
